home *** CD-ROM | disk | FTP | other *** search
/ MacHack 1997 / MacHack 1997.toast / Hacks / Hacks ’97 / Bugs in My Serial / Bugs in My Serial code / MacsBugSerialStuff.c next >
C/C++ Source or Header  |  1997-06-28  |  10KB  |  464 lines

  1. #include <lowmem.h>
  2. #include "MacsBugSerialStuff.h"
  3. #include <timer.h>
  4.  
  5. static OSErr SerialDriverConfigureSCC(void);
  6. static OSErr SerialDriverCheckForIOP(void);
  7. static OSErr SerialDriverPortAvailable(void);
  8. static pascal UInt32 SerialDriverGetMsecs(void);
  9.  
  10. enum
  11. {
  12.     kPortNotAvailable    = 1024,
  13.     kTimedOut            = 1025,
  14.     kNotImplemented        = 1026
  15. };
  16.  
  17. static void SynchronizeIO(void) = 0x4E71;        // NOP
  18.  
  19. // PORT_AVAIL_FLAG is the value in the PortAUse or PortBUse low mem
  20. // which marks a serial port as free; NUB_PORT_IN_USE_FLAG is the value
  21. // we write into that low mem when we take over a port (but if EtherTalk
  22. // is on, we have to handle port B a little differently; see below)
  23.  
  24. #define PORT_AVAIL_FLAG            0xFF
  25. #define NUB_PORT_IN_USE_FLAG    3
  26.  
  27. // The port we are using (printer or modem)
  28.  
  29. typedef enum
  30. {
  31.     kModemPort        = 0,
  32.     kPrinterPort    = 1
  33.  
  34. } SCCIOPort;
  35.  
  36. SCCIOPort        gSerialDriverPort;
  37.  
  38. // The baud rate we are using (19200 or 57600).
  39.  
  40. UInt16            gSerialDriverBaudRate;
  41.  
  42. // Whether we should time out the host connection, or hang forever.
  43.  
  44. Boolean            gSerialDriverAllowCommTimeouts;
  45.  
  46. // Whether the nub needs to concern itself with turning on the SCC.
  47.  
  48. Boolean            gSerialDriverSCCIsPowerManaged;
  49.  
  50. // Pointers to SCC read and write channels.
  51.  
  52. volatile UInt8*    gSCCWrCtlPort;
  53. volatile UInt8*    gSCCWrDataPort;
  54. volatile UInt8*    gSCCRdCtlPort;
  55. volatile UInt8*    gSCCRdDataPort;
  56.  
  57.  
  58. static OSErr SerialDriverConfigureSCC(void)
  59. {
  60.  
  61.     short i;
  62.  
  63.     // SCC configuration table
  64.     
  65.     #define SCC_CONFIG_TBL_SIZE 34
  66.     
  67.     UInt8 SCCConfigTable[] =
  68.     {
  69.          9, 0x80,        // WR  9, reset channel, SCC interrupts disabled
  70.          4, 0x44,        // WR  4, 16x clock, 1 stop bit, no parity
  71.          3, 0xC0,        // WR  3, Rx 8 bits, disabled
  72.          5, 0xE2,        // WR  5, Tx 8 bits, DTR and RTS asserted, transmitter disabled
  73.          2, 0x00,        // WR  2, zero interrupt vector
  74.         10, 0x00,        // WR 10, NRZ encoding
  75.         11, 0x50,        // WR 11, baud rate generator clock to receiver, transmitter
  76.         12, 0x00,        // WR 12, low byte baud rate (57.6K) -- 3.6864 MHz, 16x clock
  77.         13, 0x00,        // WR 13, high byte baud rate (9.6K up)
  78.          3, 0xC1,        // WR  3, Rx 8 bits, enabled
  79.          5, 0xEA,        // WR  5, Tx 8 bits, DTR and RTS asserted, transmitter enabled
  80.         14, 0x01,        // WR 14, enable baud rate generator
  81.         15, 0x80,        // WR 15, enable break/abort interrupts
  82.          0, 0x10,        // WR  0, reset ext/status interrupts
  83.          0, 0x10,        // WR  0, reset ext/status interrupts (again)
  84.          1, 0x01,        // WR  1, enable ext/status interrupts
  85.          9, 0x0A        // WR  9, SCC interrupts enabled (MIE), status in low bits
  86.     };
  87.     
  88.     // assumes that the SCC is accessible (see SerialDriverCheckForIOP below),
  89.     // and that we're called with interrupts off
  90.     
  91.     // make sure we reset the right channel
  92.  
  93.     SCCConfigTable[1] = (gSerialDriverPort == kModemPort) ? 0x80 : 0x40;
  94.  
  95.     // set the baud rate to 19200 or 57600
  96.  
  97.     SCCConfigTable[15] = (gSerialDriverBaudRate == 19200) ? 4 : 0;
  98.  
  99.     // write configuration table info to the SCC
  100.  
  101.     for (i = 0; i < SCC_CONFIG_TBL_SIZE; i += 2)
  102.     {
  103.         *gSCCWrCtlPort = SCCConfigTable[i];            // select the register
  104.         SynchronizeIO();
  105.         *gSCCWrCtlPort = SCCConfigTable[i + 1];        // write it
  106.         SynchronizeIO();
  107.     }
  108.  
  109.     return noErr;
  110.  
  111. }
  112.  
  113.  
  114. static OSErr SerialDriverCheckForIOP(void)
  115. {
  116.  
  117.     UInt8 sccRR1;
  118.     
  119.     // If we're on a machine with an IOP chip, it must be set to bypass
  120.     // mode for direct SCC access, or we'll get bus errors. To verify that
  121.     // we can really touch the SCC, we try to read RR1, which involves
  122.     // both a read and a write to the chip.
  123.     
  124.     *gSCCWrCtlPort = 1;                // select RR1
  125.     SynchronizeIO();
  126.  
  127.     sccRR1 = *gSCCRdCtlPort;        // read RR1
  128.     SynchronizeIO();
  129.  
  130.     return noErr;
  131.  
  132. }
  133.  
  134.     #pragma parameter __D0 RequestPortBFromLAPManager
  135.     OSErr RequestPortBFromLAPManager() = {0x2278, 0x0B18, 0x7011, 0x7203, 0x4EA9, 0x0002};
  136.  
  137. static    char LMGetPortAUse()
  138. {
  139.     return *((Ptr)0x290);
  140. }
  141.  
  142. static OSErr SerialDriverPortAvailable(void)
  143. {
  144.  
  145.     // 0x2278, 0x0B18,        // MOVEA.L    LAPMgrPtr,A1
  146.     // 0x7011,                // MOVEQ    #LUsePortB,D0
  147.     // 0x7203,                // MOVEQ    #3,D1
  148.     // 0x4EA9, 0x0002,        // JSR        LAPMgrCall(A1)
  149.     
  150.  
  151.     // Mark our chosen port as in use.
  152.  
  153.     if ((gSerialDriverPort == kModemPort) && (LMGetPortAUse() == PORT_AVAIL_FLAG))
  154.     {
  155. //        LMSetPortAUse(NUB_PORT_IN_USE_FLAG);
  156.         return noErr;
  157.     }
  158.  
  159.     if (gSerialDriverPort == kPrinterPort)
  160.     {
  161.         if (LMGetPortBUse() == PORT_AVAIL_FLAG)
  162.         {
  163. //            LMSetPortBUse(NUB_PORT_IN_USE_FLAG);
  164.             return noErr;
  165.         }
  166.         else
  167.         {
  168.             return RequestPortBFromLAPManager();
  169.         }
  170.     }
  171.  
  172.     return kPortNotAvailable;
  173.  
  174. }
  175.  
  176.  
  177. pascal OSErr SerialDriver_Open(void)
  178. {
  179.  
  180.     short    ctlPortOffset;
  181.     short    dataPortOffset;
  182.     OSErr    result;
  183.     long    gestaltResponse;
  184.     
  185.     // ••• these should be configurable
  186.     
  187.     gSerialDriverPort = kPrinterPort;                    // ••• kModemPort for single-port PowerBooks
  188.     gSerialDriverBaudRate = 57600;
  189.     gSerialDriverAllowCommTimeouts = false;                // for now, this should never be true (no timer)
  190.     gSerialDriverSCCIsPowerManaged = false;                // assume we're not on a power-tasic machine
  191.     
  192.     // Make sure we're not colliding with LocalTalk or somebody else.
  193.  
  194.     if (SerialDriverPortAvailable() != noErr)
  195.     {
  196.         return kPortNotAvailable;
  197.     }
  198.     
  199.     // Set up pointers to SCC read and write channels.
  200.  
  201.      ctlPortOffset = (gSerialDriverPort == kModemPort) ? 2 /* aCtl */ : 0 /* bCtl */;
  202.     dataPortOffset = (gSerialDriverPort == kModemPort) ? 6 /* aData */ : 4 /* bData */;
  203.  
  204.     gSCCWrCtlPort = (UInt8*)LMGetSCCWr() + ctlPortOffset;
  205.     gSCCWrDataPort =  (UInt8*)LMGetSCCWr() + dataPortOffset;
  206.     gSCCRdCtlPort =  (UInt8*)LMGetSCCRd() + ctlPortOffset;
  207.     gSCCRdDataPort =  (UInt8*)LMGetSCCRd() + dataPortOffset;
  208.  
  209.     // Make sure we can access the SCC directly.
  210.  
  211. //    if ((result = SerialDriverCheckForIOP()) != noErr)
  212. //    {
  213. //        return result;
  214. //    }
  215.     
  216.     // If we are on a machine with a Power Managed SCC then we need to record that so
  217.     // we can make sure it is powered up each time we are entered.
  218.  
  219.     result = Gestalt(gestaltPowerMgrAttr, &gestaltResponse);
  220.     
  221.     if (result == noErr)
  222.     {
  223.         // First make sure the Power Mgr API is available on this box.
  224.  
  225.         if (gestaltResponse & (1 << gestaltPMgrDispatchExists))
  226.         {
  227.             // Now find out if the SCC is power managed and save an indication for later.
  228.  
  229.             if (gestaltResponse & (1 << gestaltPMgrSCC))
  230.             {
  231.                 gSerialDriverSCCIsPowerManaged = true;
  232.             }    
  233.         }
  234.     }
  235.  
  236.     result = SerialDriver_EnableSCCPower();
  237.  
  238.     // This is necessary for machines with non-power managed SCCs. SerialDriver_EnableSCCPower
  239.     // does nothing on those machines.
  240.  
  241.     if (result == noErr)
  242.     {
  243.         result = SerialDriver_Reset();
  244.  
  245.         SerialDriver_SendString("Welcome to MacsBug\n\n");
  246.     }
  247.  
  248.     return result;
  249.  
  250. }
  251.  
  252.  
  253. pascal OSErr SerialDriver_Reset()
  254. {
  255.  
  256.     // This assumes SerialDriver_StartIO has already been called, which is true since
  257.     // each time it is called, we are called.
  258.  
  259.     return SerialDriverConfigureSCC();
  260.  
  261. }
  262.  
  263.  
  264. pascal OSErr SerialDriver_EnableSCCPower()
  265. {
  266.     return noErr;
  267. /*
  268.     // This assumes SerialDriver_StartIO has already been called, which is true since
  269.     // each time it is called, it calls us.
  270.  
  271.     if (gSerialDriverSCCIsPowerManaged)
  272.     {
  273.         if (gSerialDriverPort == kModemPort)
  274.         {
  275.             AOnIgnoreModem();
  276.         }
  277.         else
  278.         {
  279.             BOn();
  280.         }
  281.  
  282.         // Since we've just (possibly) turned on the power to the SCC, we need to
  283.         // reinitialize it.
  284.         
  285.         return SerialDriver_Reset();
  286.     }
  287.  
  288.     // If the machine does not have power managed serial ports, we do nothing.
  289.  
  290.     return noErr;
  291. */
  292. }
  293.  
  294.  
  295. pascal Boolean SerialDriver_BytePresent()
  296. {
  297.  
  298.     UInt8 sccRR0 = *gSCCRdCtlPort;
  299.     
  300.     SynchronizeIO();
  301.  
  302.     return sccRR0 & 0x01;
  303.  
  304. }
  305.  
  306.  
  307. static pascal UInt32 SerialDriverGetMsecs()
  308. {
  309.  
  310. //•••    return (SerialDriverGetTime() / 10);        // returns tenths of a msec
  311.  
  312.     return 0;
  313.  
  314. }
  315.  
  316.  
  317. pascal Boolean SerialDriver_WaitForByte(UInt16 timeoutMillisecs)
  318. {
  319.  
  320.     UInt8        sccRR0;
  321.     UInt32        currentTime, cutoffTime;
  322.     
  323.     if (gSerialDriverAllowCommTimeouts)
  324.     {
  325.         currentTime = SerialDriverGetMsecs();
  326.         cutoffTime = currentTime + timeoutMillisecs;
  327.     }
  328.     
  329.     while (1)        // wait until there's a byte there (or we time out)
  330.     {
  331.         sccRR0 = *gSCCRdCtlPort;
  332.         SynchronizeIO();
  333.  
  334.         if (sccRR0 & 0x01)
  335.         {
  336.             // We got a byte.
  337.  
  338.             return true;
  339.         }
  340.  
  341.         if (gSerialDriverAllowCommTimeouts)
  342.         {
  343.             // Still waiting - see if we've timed out.
  344.  
  345.             if (SerialDriverGetMsecs() > cutoffTime)
  346.             {
  347.                 return false;    // we timed out waiting for a byte
  348.             }
  349.         }
  350.     }
  351.  
  352. }
  353.  
  354.  
  355. pascal OSErr SerialDriver_ReceiveByte(UInt8 *inByte, UInt16 timeoutMillisecs)
  356. {
  357.  
  358.     UInt8 recvdByte;
  359.     
  360.     if (!SerialDriver_WaitForByte(timeoutMillisecs))
  361.     {
  362.         return kTimedOut;
  363.     }
  364.     
  365.     recvdByte = *gSCCRdDataPort;
  366.     SynchronizeIO();
  367.  
  368.     *inByte = recvdByte;
  369.     
  370.     return noErr;
  371.  
  372. }
  373.  
  374.  
  375. void SerialDriver_SendString(const char *, ...)
  376. {
  377.  
  378.     // Ideally, we'd allow printf-style format strings through this function. However, calling
  379.     // any of the XXprintf functions brings in statically initialized data, which we can't have.
  380.     // I really should fix MacsBug to allow it.
  381.     
  382. //    va_list    args;
  383. //    char    localString[512];
  384.     
  385. //    va_start(args, format);
  386. //    vsprintf(localString, format, args);
  387. //    va_end(args);
  388.  
  389. //    SerialDriver_SendBytes(format, strlen(format));
  390.  
  391. }
  392.  
  393.  
  394. pascal void SerialDriver_SendBytes(UInt8 *outBytes, UInt16 count)
  395. {
  396.  
  397.     UInt8 sccRR0, theByte;
  398.  
  399.     while (count--)
  400.     {
  401.         // Make sure the transmitter is idle.
  402.         
  403.         do
  404.         {
  405.             sccRR0 = *gSCCRdCtlPort;
  406.             SynchronizeIO();
  407.         }
  408.         while ((sccRR0 & 0x04) == 0);
  409.  
  410.         theByte = *outBytes++;
  411.  
  412.         *gSCCWrDataPort = theByte;
  413.         SynchronizeIO();
  414.     }
  415.     
  416.  
  417. }
  418.  
  419.  
  420. pascal OSErr SerialDriver_FlushIncomingBuffers()
  421. {
  422.  
  423.     UInt8 sccRR0, recvdByte;
  424.  
  425.     // Flush the on-chip receive buffer.
  426.     
  427.     do
  428.     {
  429.         sccRR0 = *gSCCRdCtlPort;
  430.         SynchronizeIO();
  431.  
  432.         if (sccRR0 & 0x01)
  433.         {
  434.             recvdByte = *gSCCRdDataPort;
  435.             SynchronizeIO();
  436.         }
  437.     }
  438.     while (sccRR0 & 0x01);
  439.     
  440.     return noErr;
  441.  
  442. }
  443.  
  444.  
  445. pascal OSErr SerialDriver_SendBreak()
  446. {
  447.  
  448.     return kNotImplemented;
  449.  
  450. }
  451.  
  452.  
  453. pascal Boolean SerialDriver_BreakPending()
  454. {
  455.  
  456.     UInt8 sccRR0;
  457.     
  458.     sccRR0 = *gSCCRdCtlPort;
  459.     SynchronizeIO();
  460.     
  461.     return ((sccRR0 & 0x80) != 0);
  462.  
  463. }
  464.